﻿#include <QDebug.h>
#include <QMessageBox.h>
#include <QDateTime.h>
#include <QDir>
#include <QUrl>
#include <Qvector>
#include <QDesktopServices>
#include <QUrl>
#include <QMutex>
#include <QApplication>
#include <QSettings>
//#include <QTextCodec>
#include <QtSerialPort/QSerialPort>
#include <QtSerialPort/QSerialPortInfo>
#include <QUdpSocket>//UDP套接字
#include <QFileDialog>  //for getOpenFileName()
//#include <QStringList>
#include <QDir>
#include <QFileInfo>
#include <QFileSystemModel>
#include <QProcess>
#include <QRegExp>
#include <QTextStream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h> 
#include <Windows.h>

#include "HPDAQ_GUI.h"
#include "my_pub.h"

// 主控板
#define ZK_CFG_LEN (1024*1024*2)
u8 zk_cfg_buf[ZK_CFG_LEN];

// 菜单
#define MENU_IP_COUNT 8
QString qs_menu_ip_group[MENU_IP_COUNT] =
{
	"IDC_MENU_SET_MB_CH1",
	"IDC_MENU_SET_MB_CH2",
	"IDC_MENU_SET_MB_CH3",
	"IDC_MENU_SET_MB_CH4",
	"IDC_MENU_SET_MB_CH5",
	"IDC_MENU_SET_MB_CH6",
	"IDC_MENU_SET_MB_CH7",
	"IDC_MENU_SET_MB_CH8"
};

#define MENU_BOARD_COUNT 3
QString qs_menu_board_group[MENU_BOARD_COUNT] =
{
	"IDC_MENU_SET_BOARD_JIEKOU",
	"IDC_MENU_SET_BOARD_ADDA",
	"IDC_MENU_SET_BOARD_HV"
};
QString qs_board_name[MENU_BOARD_COUNT] = {
	cs("接口板"),
	cs("AD/DA板"),
	cs("HV板")
};

///////////////////////////////////////////////////////////////////////////////////////
// 全局变量
///////////////////////////////////////////////////////////////////////////////////////
const QString FILE_CFG_INI = "COMMANDER_CFG.INI";
u8 b_auto_get = 0;
para_struct para;
u32 this_count = 0;
QMutex mutex;
FILE *fp_data_bin = NULL;

//cmdlist
enum CMD_LIST_RUN_STATE
{
	S_IDLE = 0,
	S_RUN,
	S_PAUSE
};

cmdlist_struct cmdlist;
enum CMD_LIST_RUN_STATE cmdlist_state = S_IDLE;
bool b_auto_cmdlist = 0;
bool b_cmdlist_run_flag = true;
void my_thread_for_cmdlist_run(LPVOID lpram);

///////////////////////////////////////////////////////////////////////////////////////
// UDP组播相关
///////////////////////////////////////////////////////////////////////////////////////
bool is_udp_init_done = false;
QUdpSocket *udpSocket = NULL;//UDP套接字
QString s_mcast_ip="239.255.10.1";  // 组播地址(239.0.0.0～239.255.255.255)
static QHostAddress g_mcast_addr; // 
#define MULT_PORT 59999
#define UDP_LEN 1024
typedef struct struct_udp_packet_t
{
	u16 len;
	u8 udp_buf[UDP_LEN];
}struct_udp_packet;
struct_udp_packet udp_packet;

/////////////////////////////////////////////////////////
// 串口相关
/////////////////////////////////////////////////////////
#define COM_SEL_MAX 20
COMSTAT state;
HANDLE h_com = INVALID_HANDLE_VALUE;

u8 tx_buf[CMD_LEN] = { 0xEB, 0x90, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00 };
//#define RSP_LEN 205   // 接口板
//#define RSP_LEN 425       //ADDA板
u16 rsp_len_buf[] = {
	205,  // 接口板
	425,  // ADDA板
	225   // HV板
};

u16 RSP_LEN = 1024;
u8 rx_buf[1024] = { 0 };

#define SHOW_CMD_TO_LOG(tx_buf) do{\
	u8 i; \
	QString qs=QString::fromLocal8Bit("发送指令=["); \
	for (i = 0; i < CMD_LEN; i++) \
        qs=qs+QString::asprintf(" %02X",tx_buf[i]); \
	qs=qs+"]";  \
	Show_Log(qs);  \
}while(0)


#define SHOW_REPLY_TO_LOG(rx_buf) do{\
	u16 i; \
	QString qs=QString::fromLocal8Bit("接收工参=["); \
	for (i = 0; i < RSP_LEN; i++) \
		qs=qs+QString::asprintf(" %02X",rx_buf[i]); \
	qs=qs+"]";  \
	Show_Log(qs);  \
}while(0)

///////////////////////////////////////////////////////////////////////////////////////
// Show_Log的实现
///////////////////////////////////////////////////////////////////////////////////////
class MyLogEvent : public QEvent
{
public:
	MyLogEvent(QEvent::Type type, QString qstr) :QEvent(type) { m_qstr = qstr; }
	~MyLogEvent() {}
	QString m_qstr;
};
static const QEvent::Type EVENT_TYPE_ALLLOG = static_cast<QEvent::Type>(QEvent::registerEventType());
static const QEvent::Type EVENT_TYPE_CMDLOG = static_cast<QEvent::Type>(QEvent::registerEventType());

static FILE *fp_log = NULL;
static FILE *fp_cmd_log = NULL;
static QDateTime cur_qtime;
static QString s_cur_time;
#define GetCurTime() do{\
	cur_qtime = QDateTime::currentDateTime();\
	s_cur_time = cur_qtime.toString("(hh:mm:ss)> "); \
}while(0)

void HPDAQ_GUI::Show_Log(QString qstr)
{
	QApplication::postEvent(this, new MyLogEvent(EVENT_TYPE_ALLLOG, qstr));
}

void HPDAQ_GUI::Show_Cmd_Log(QString qstr)
{
	QApplication::postEvent(this, new MyLogEvent(EVENT_TYPE_CMDLOG, qstr));
}

void HPDAQ_GUI::Show_Log(const char *fmt, ...)
{
	va_list ap;
	static QMutex qm;
	static char s_buf[1024];

	qm.lock();
	va_start(ap, fmt);
	vsprintf(s_buf, fmt, ap);
	va_end(ap);

	Show_Log(QString::fromLocal8Bit(s_buf));
	qm.unlock();
}

void HPDAQ_GUI::Show_Hex_Info(QString pre_qs, u8 *pbuf, u32 len)
{
	static QMutex qm;
	QString qs;
	u32 i;

	qm.lock();
	qs = pre_qs;
	if (len <= 24)
	{
		for (i = 0; i < len; i++)
			qs += QString::asprintf(" %02X", pbuf[i]);
	}
	else
	{
		for (i = 0; i < 16; i++)
			qs += QString::asprintf(" %02X", pbuf[i]);
		qs += "......";
		qs += QString::asprintf(" %02X %02X %02X %02X %02X %02X %02X %02X",
			pbuf[len - 8], pbuf[len - 7], pbuf[len - 6], pbuf[len - 5],
			pbuf[len - 4], pbuf[len - 3], pbuf[len - 2], pbuf[len - 1]);
	}
	Show_Log(qs);
	qm.unlock();
}

////////////////////////////////////////////////////
// GUI Update for Show_Log, called by event()
///////////////////////////////////////////////////
void HPDAQ_GUI::Show_Log_Event_Handle(QString s)
{
	static int item_count = 0;
	
	GetCurTime();

	if (fp_log == NULL)
	{
		QString s = Check_And_Create_Dir("log");
		QString log_file_name;
		log_file_name = cur_qtime.toString("DyyyyMMdd_Thhmmss.log");
		s = s + "/" + log_file_name;
		fp_log = fopen((const char*)s.toLocal8Bit(), "w");
		Show_Log("Log File Path:");
		Show_Log("    " + s);
	}
	if (item_count++ > 1000)
	{
		ui.IDC_TEXT_LOG->clear();
		item_count = 0;
	}
	ui.IDC_TEXT_LOG->append(s_cur_time + s);
	QByteArray temp_byte_array = (s_cur_time + s + "\n").toLocal8Bit();
	char *temp_char = temp_byte_array.data();
	fprintf(fp_log, temp_char);
	fflush(fp_log);
}

void HPDAQ_GUI::Show_Cmd_Log_Event_Handle(QString s)
{
	static int item_count = 0;

	GetCurTime();

	if (fp_cmd_log == NULL)
	{
		QString s = Check_And_Create_Dir("cmd_log");
		QString log_file_name;
		log_file_name = cur_qtime.toString("DyyyyMMdd_Thhmmss.cmdlog");
		s = s + "/" + log_file_name;
		fp_cmd_log = fopen((const char*)s.toLocal8Bit(), "w");
	}
	if (item_count++ > 1000)
	{
		ui.IDC_TEXT_CMD_LOG->clear();
		item_count = 0;
	}
	ui.IDC_TEXT_CMD_LOG->append(s_cur_time + s);
	QByteArray temp_byte_array = (s_cur_time + s + "\n").toLocal8Bit();
	char *temp_char = temp_byte_array.data();
	fprintf(fp_cmd_log, temp_char);
	fflush(fp_cmd_log);
}

////////////////////////////////////////////////////
// event() reload function, to handle the event from postEvent
///////////////////////////////////////////////////
bool HPDAQ_GUI::event(QEvent *e)
{
	if (e->type() == EVENT_TYPE_ALLLOG)  /* It's MyLogEvent */
	{
		Show_Log_Event_Handle((dynamic_cast<MyLogEvent*>(e))->m_qstr);
	}
	if (e->type() == EVENT_TYPE_CMDLOG)  /* It's MyLogEvent */
	{
		Show_Cmd_Log_Event_Handle((dynamic_cast<MyLogEvent*>(e))->m_qstr);
	}
	return QWidget::event(e);
}

//Create a directory at the exe path
QString HPDAQ_GUI::Check_And_Create_Dir(QString dir_name)
{
	QString sPath;

	//Get the path where myself(this *.exe programe) at.
	sPath = QCoreApplication::applicationDirPath();

	// create the directory if it dosn't exist
	sPath = sPath + "/" + dir_name;
	QDir *tempDir = new QDir;
	if (!tempDir->exists(sPath))
	{
		QDir dir(sPath);
		dir.mkdir(sPath);
	}
	return sPath;
}
///////////////////////////////////////////////////////////////////////////////////////

HPDAQ_GUI::HPDAQ_GUI(QWidget *parent)
    : QMainWindow(parent)
{
    ui.setupUi(this);
	InitWindow();
}

void HPDAQ_GUI::InitWindow()
{
	u16 i;
	QString s;
	
	// Set the title
	setWindowTitle(main_title_str);
	Show_Log("");

	fs_model = NULL;	

	// Init combox
	for (i = 0; i<COM_SEL_MAX; i++)
		ui.IDC_COMBO_UART->addItem(mk_qstr("COM%d",i));
	
	Load_Usr_Conf();   // 加载用户上次的配置
	Show_Usr_Conf(); 	// 显示配置
	Show_Cmd_Lib(para.cmdlib_path);  // 显示指令库
	InitTableView_For_CMDPed();
	Slot_OnBtnCmdPedClr(); //清空指令列

	ui.IDC_EDIT_CMD_TYPE->setText("01");
	ui.IDC_EDIT_CMD_DATA->setText("00000000");
	
	ui.IDC_TAB_BUS->setCurrentIndex(0);
	ui.IDC_TAB_LOG->setCurrentIndex(0);

	//开启定时器, 自动调用timerEvent(QTimerEvent *t)
	timer_id = startTimer(1000);

	// connect the slots
	connect(ui.IDC_BTN_DEBUG, SIGNAL(clicked()), this, SLOT(Slot_OnBtnDebug()));
	connect(ui.IDC_BTN_UART_OPEN, SIGNAL(clicked()), this, SLOT(Slot_OnBtnUartOpen()));
	connect(ui.IDC_BTN_UART_CLOSE, SIGNAL(clicked()), this, SLOT(Slot_OnBtnUartClose()));
	connect(ui.IDC_BTN_CMD_SEND, SIGNAL(clicked()), this, SLOT(Slot_OnBtnCmdSend()));
	connect(ui.IDC_BTN_GETSTAT, SIGNAL(clicked()), this, SLOT(Slot_OnBtnGetStat()));
	connect(ui.IDC_BTN_CMDDIR_BROSE, SIGNAL(clicked()), this, SLOT(Slot_OnBtnCmddirBrose()));
	connect(ui.IDC_BTN_CMDPED_CLR, SIGNAL(clicked()), this, SLOT(Slot_OnBtnCmdPedClr()));
	connect(ui.IDC_BTN_CMDPED_ONCERUN, SIGNAL(clicked()), this, SLOT(Slot_OnBtnCmdPedOnceRun()));
	connect(ui.IDC_BTN_CMDPED_LISTRUN, SIGNAL(clicked()), this, SLOT(Slot_OnBtnCmdPedListRun()));
	connect(ui.IDC_BTN_CMDPED_LISTSTOP, SIGNAL(clicked()), this, SLOT(Slot_OnBtnCmdPedListStop()));
	connect(ui.IDC_BTN_CMDPED_LISTPAUSECONT, SIGNAL(clicked()), this, SLOT(Slot_OnBtnCmdPedListPauseCont()));
	connect(ui.IDC_ZK_BTN_CFG_TXT2BIN, SIGNAL(clicked()), this, SLOT(Slot_OnZKBtnCfgTxt2Bin()));
	
	connect(ui.IDC_CHECK_GETSTAT, SIGNAL(stateChanged(int)), this, SLOT(Slot_OnCkGetstatStateChanged(int)));
			

	QAction  *qmu;

	// 将菜单/IP选择connect到同一个slot
	for (i = 0; i < MENU_IP_COUNT; i++)
	{
		qmu = this->findChild<QAction *>(qs_menu_ip_group[i]);
		connect(qmu, SIGNAL(triggered()), this, SLOT(Slot_MenuSetMBCh()));
	}

	// 将菜单/板卡选择connect到同一个slot
	for (i = 0; i < MENU_BOARD_COUNT; i++)
	{
		qmu = this->findChild<QAction *>(qs_menu_board_group[i]);
		connect(qmu, SIGNAL(triggered()), this, SLOT(Slot_MenuSetBoardSel()));
	}

	// 创建自定义Thread
	p_thread = new MyThread(this);
	p_thread->set_func(&my_thread_for_cmdlist_run);
	p_thread->start();
	connect(p_thread, &MyThread::Sig_thread_data, this, &HPDAQ_GUI::Slot_cmdlist_thread_func_data);
	connect(p_thread, &MyThread::Sig_show_log, this, &HPDAQ_GUI::Slot_cmdlist_thread_func_log);
}

// 初始化cmdped列表
void HPDAQ_GUI::InitTableView_For_CMDPed()
{
	u16 i;
	p_view_cmdped = new QStandardItemModel(CMDLIST_MAX_LEN, 4, this);
	ui.IDC_TABLE_VIEW_CMDPED->setModel(p_view_cmdped);

	// 显示行表头和列表头
	ui.IDC_TABLE_VIEW_CMDPED->horizontalHeader()->setVisible(true);
	ui.IDC_TABLE_VIEW_CMDPED->verticalHeader()->setVisible(true);

	// 设置标题颜色
	ui.IDC_TABLE_VIEW_CMDPED->horizontalHeader()->setStyleSheet("QHeaderView::section {"
		"color: black;padding-left: 4px;border: 1px solid #6c6c6c;}");
	ui.IDC_TABLE_VIEW_CMDPED->verticalHeader()->setStyleSheet("QHeaderView::section {"
		"color: black;padding-left: 4px;border: 1px solid #6c6c6c;}");

	//设置行标题和行宽度
	for (i = 0; i < CMDLIST_MAX_LEN; i++)
	{
		p_view_cmdped->setVerticalHeaderItem(i, new QStandardItem(QString::asprintf("%d", i)));
		ui.IDC_TABLE_VIEW_CMDPED->setRowHeight(i, 25);
	}		
	
	//设置列标题和列宽度
	p_view_cmdped->setHorizontalHeaderItem(0, new QStandardItem(cs("间隔(s)")));
	p_view_cmdped->setHorizontalHeaderItem(1, new QStandardItem(cs("指令名称")));
	p_view_cmdped->setHorizontalHeaderItem(2, new QStandardItem(cs("指令源码")));
	p_view_cmdped->setHorizontalHeaderItem(3, new QStandardItem(cs("指令执行时间")));
	ui.IDC_TABLE_VIEW_CMDPED->setColumnWidth(0, 80);
	ui.IDC_TABLE_VIEW_CMDPED->setColumnWidth(1, 300);
	ui.IDC_TABLE_VIEW_CMDPED->setColumnWidth(2, 300);
	ui.IDC_TABLE_VIEW_CMDPED->setColumnWidth(3, 200);

	//设置表格的单元为只读属性，即不能编辑  
	ui.IDC_TABLE_VIEW_CMDPED->setEditTriggers(QAbstractItemView::NoEditTriggers);

	//设置选中则选中一整行
	ui.IDC_TABLE_VIEW_CMDPED->setSelectionBehavior(QAbstractItemView::SelectRows);
	
	//只能选择单行/禁止选择多行
	ui.IDC_TABLE_VIEW_CMDPED->setSelectionMode(QAbstractItemView::SingleSelection);

	//初始化数据内容
	for (i = 0; i < CMDLIST_MAX_LEN; i++)
	{
		for (u8 j = 0; j < 4; j++)
		{
			p_view_cmdped->setItem(i, j, new QStandardItem("--"));
			p_view_cmdped->item(i, j)->setTextAlignment(Qt::AlignCenter);  //设置居中对齐
		}
	}
}

// 显示cmdlist
void HPDAQ_GUI::Show_CmdPed()
{
	u16 i;
	QString qs;
	for (i = 0; i < CMDLIST_MAX_LEN; i++)
	{
		if (cmdlist.item[i].is_en)
		{
			p_view_cmdped->item(i, 0)->setText(QString::asprintf("%d", cmdlist.item[i].gap));
			p_view_cmdped->item(i, 1)->setText(cmdlist.item[i].cname);
			qs = "";
			for (u8 k = 0; k < CMD_LEN; k++)
				qs = qs + mk_qstr(" %02X", cmdlist.item[i].data[k]);
			p_view_cmdped->item(i, 2)->setText(qs);
			p_view_cmdped->item(i, 3)->setText(QString::asprintf("--"));
		}
		else  // 如果未使能
		{
			p_view_cmdped->item(i, 0)->setText(QString::asprintf("--"));
			p_view_cmdped->item(i, 1)->setText(QString::asprintf("--"));
			p_view_cmdped->item(i, 2)->setText(QString::asprintf("--"));
			p_view_cmdped->item(i, 3)->setText(QString::asprintf("--"));
		}
	}
}

//保存本次配置数据
void HPDAQ_GUI::Save_Usr_Conf()
{
	//Get the path where myself(this *.exe programe) at.
	QString cfg_full_dir = QCoreApplication::applicationDirPath() + "\\" + FILE_CFG_INI;
	QSettings cfg(cfg_full_dir, QSettings::IniFormat);

	// Save
	cfg.setValue("COM/com_port_sel", para.com_port_sel);
	cfg.setValue("CMD/cmdlib_path", para.cmdlib_path);
	cfg.setValue("IP/ip_n", para.ip_n);
	cfg.setValue("BOARD/board_n", para.board_n);
}

//加载上次配置数据
void HPDAQ_GUI::Load_Usr_Conf()
{
	//Get the path where myself(this *.exe programe) at.
	QString cfg_full_dir = QCoreApplication::applicationDirPath() + "\\" + FILE_CFG_INI;
	QSettings cfg(cfg_full_dir, QSettings::IniFormat);

	// Load
	para.com_port_sel=cfg.value("COM/com_port_sel", 1).toInt();
	para.cmdlib_path = cfg.value("CMD/cmdlib_path", cs("./指令库")).toString();
	para.ip_n = cfg.value("IP/ip_n", 0).toInt();
	para.board_n = cfg.value("BOARD/board_n", 0).toInt();
	
	// Load check
	if (para.com_port_sel >= COM_SEL_MAX)
		para.com_port_sel = 1;

	if ((para.ip_n >= MENU_IP_COUNT)||(para.ip_n<0))
		para.ip_n = 0;

	if ((para.board_n >= MENU_BOARD_COUNT) || (para.board_n<0))
		para.board_n = 0;
}

//显示上次配置数据
void HPDAQ_GUI::Show_Usr_Conf()
{
	ui.IDC_COMBO_UART->setCurrentIndex(para.com_port_sel);
	SetMenuIpSelected(para.ip_n);
	SetMenuBoardSelected(para.board_n);
}
/////////////////////////////////////////////////
// Following for all slots
/////////////////////////////////////////////////
//Btn: Uart Open
void HPDAQ_GUI::Slot_OnBtnUartOpen()
{
	QString str;
	QString str_port;
	int rate;

	//准备参数
	para.com_port_sel = ui.IDC_COMBO_UART->currentIndex();

	//打开串口
	str_port=mk_qstr("COM%d", para.com_port_sel);
	rate = 115200;
	Save_Usr_Conf();

	if (h_com != INVALID_HANDLE_VALUE)
	{
		Show_Log("Error: Please Close Serial Port and try again !");
		return;
	}
	h_com = CreateFileW(str_port.toStdWString().data(), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);


	if (h_com == INVALID_HANDLE_VALUE)
		Show_Log("Open Serial Port(COM%d) Failed，Error code= %d. ", para.com_port_sel, GetLastError());
	else
		Show_Log("Open Serial Port (COM%d) Success! ", para.com_port_sel);

	//Get UART Port Configurations
	DCB dcb;
	BOOL bSuccess;
	bSuccess = GetCommState(h_com, &dcb);
	if (!bSuccess)
	{// Handle the error.
		Show_Log("GetCommState(COM%d) failed with error code = %d. ", para.com_port_sel, GetLastError());
		return;
	}

	//Modify Configurations
	dcb.BaudRate = rate;    // set the baud rate
	dcb.ByteSize = 8;           // data size, xmit, and rcv
								//dcb.Parity = NOPARITY; //ODDPARITY;      // no parity bit
	dcb.Parity = ODDPARITY; //ODDPARITY;      // no parity bit
							//	dcb.StopBits = ONESTOPBIT; // one stop bit
	dcb.StopBits = TWOSTOPBITS; // two stop bit
	bSuccess = SetCommState(h_com, &dcb);
	if (!bSuccess)
		str= mk_qstr("SetCommState(COM%d) failed with error code = %d. ", para.com_port_sel, GetLastError());
	else
		str=mk_qstr("Set Serial Port(COM%d) : %d,8,odd,2-bit stop", para.com_port_sel, rate);
	Show_Log(str);
}

//Btn: Uart Close
void HPDAQ_GUI::Slot_OnBtnUartClose()
{
	if (h_com == INVALID_HANDLE_VALUE)
	{
		Show_Log("Close Serial Port Failed: Not Opened!");
		return;
	}
	CloseHandle(h_com);
	h_com = INVALID_HANDLE_VALUE;
	Show_Log("Close Serial Port Done!");
}

//Btn: 读取工参
void HPDAQ_GUI::Slot_OnBtnGetStat()
{
	if (!b_auto_get)
		get_stat();
	else
		Show_Log("由于当前处于自动获取工参状态，忽略本次手动工参请求..");
}

// 按钮槽函数：更改指令库
void HPDAQ_GUI::Slot_OnBtnCmddirBrose()
{
	QString mydir;
	mydir = QFileDialog::getExistingDirectory(
		this,
		cs("打开指令库所在的文件夹"),
		para.cmdlib_path,  // 当前默认路径
		QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);

	if (mydir.isEmpty()) {
		Show_Log("Warning: 未选择文件夹.");
		return;
	}
	else
	{
		para.cmdlib_path = mydir;
		Show_Log(cs("选择了指令库文件夹: ") + mydir);
		Save_Usr_Conf();
		Show_Cmd_Lib(para.cmdlib_path);
	}
}


// 槽函数：按钮清除Ped指令
void HPDAQ_GUI::Slot_OnBtnCmdPedClr()
{
	u16 i;
	cmdlist.tot_cnt = 0;
	cmdlist.cur_idx = 0;
	for (i = 0; i < CMDLIST_MAX_LEN; i++)
	{
		cmdlist.item[i].is_en = 0;
		cmdlist.item[i].gap = 1;
		memset(cmdlist.item[i].data,0,CMD_LEN);
		cmdlist.item[i].cname = "";
	}
	Show_CmdPed();
}

// 槽函数: 处理cmdlist的data
void HPDAQ_GUI::Slot_cmdlist_thread_func_data(int para)
{
	int i;
	i = para;
	Send_Uart_Cmd(cmdlist.item[i].data, CMD_LEN);
	Put_Cmd_Log(cmdlist.item[i].cname, cmdlist.item[i].data, 1, i);
}

// 槽函数: 处理cmdlist的log消息
void HPDAQ_GUI::Slot_cmdlist_thread_func_log(QString qstr)
{
	Show_Log(qstr);
}

// 设置对应的Menu/IP选择
void HPDAQ_GUI::SetMenuIpSelected(u8 n)
{
	QAction  *qmu;

	if (n >= MENU_IP_COUNT)
		n = 0;
	for (u8 i = 0; i < MENU_IP_COUNT; i++)
	{
		qmu = this->findChild<QAction *>(qs_menu_ip_group[i]);
		if (i == n)
		{
			qmu->setChecked(1);
			s_mcast_ip = QString("239.235.10.%1").arg(i + 1);
			is_udp_init_done = false;
			Show_Log(mk_qstr("组播地址设置为:")+ s_mcast_ip);
		}
		else
			qmu->setChecked(0);
	}
}

// 设置对应的Menu/IP选择
void HPDAQ_GUI::SetMenuBoardSelected(u8 n)
{
	QAction  *qmu;

	if (n >= MENU_BOARD_COUNT)
		n = 0;
	for (u8 i = 0; i < MENU_BOARD_COUNT; i++)
	{
		qmu = this->findChild<QAction *>(qs_menu_board_group[i]);
		if (i == n)
		{
			qmu->setChecked(1);
			RSP_LEN = rsp_len_buf[i];
			Show_Log(mk_qstr("板卡选择为:") + qs_board_name[i]+mk_qstr(", 工参长度为:%d",RSP_LEN));
		}
		else
			qmu->setChecked(0);
	}
}

// 槽函数: 菜单/设置/组播地址
void HPDAQ_GUI::Slot_MenuSetMBCh()
{
	u8 i=0;
	QObject *obj = QObject::sender();
	QAction  *qmu = qobject_cast<QAction *>(obj);
	
	for (i = 0; i < MENU_IP_COUNT; i++)
	{
		if (qmu->objectName() == qs_menu_ip_group[i])
			break;
	}
	if (i >= MENU_IP_COUNT)
		i = 0;
	
	para.ip_n = i;
	Save_Usr_Conf();
	SetMenuIpSelected(i);
}

// 槽函数: 菜单/设置/板子选择
void HPDAQ_GUI::Slot_MenuSetBoardSel()
{
	u8 i = 0;
	QObject *obj = QObject::sender();
	QAction  *qmu = qobject_cast<QAction *>(obj);

	for (i = 0; i < MENU_BOARD_COUNT; i++)
	{
		if (qmu->objectName() == qs_menu_board_group[i])
			break;
	}
	if (i >= MENU_BOARD_COUNT)
		i = 0;
	para.board_n = i;
	Save_Usr_Conf();
	SetMenuBoardSelected(i);
}

// 指令执行序列的线程函数
QMutex qm;
void my_thread_for_cmdlist_run(LPVOID lpram)
{
	int64_t last_ts = 0;
	int64_t this_ts = 0;
	int64_t delta;
	bool last_b_auto_cmdlist = false;
	MyThread *t = (MyThread *)lpram;
	qm.lock();
	while (b_cmdlist_run_flag)
	{
		Sleep(100);

		last_b_auto_cmdlist = b_auto_cmdlist;

		if (!b_auto_cmdlist)
			continue;

		if ((cmdlist.cur_idx >= cmdlist.tot_cnt) || (cmdlist.item[cmdlist.cur_idx].is_en != 1))
		{
			b_auto_cmdlist = false;
			emit t->Sig_show_log("cmdlist done!");
			cmdlist_state = S_IDLE;
			cmdlist.cur_idx++;
			continue;
		}
		QDateTime currentTime = QDateTime::currentDateTime();
		this_ts = currentTime.toTime_t();
		delta = this_ts - last_ts;

		if (delta >= cmdlist.item[cmdlist.cur_idx].gap) // 时间间隔满足，则发送指令
		{
			emit t->Sig_thread_data(cmdlist.cur_idx);
			last_ts = this_ts;
			cmdlist.cur_idx++;
		}
	}
	qm.unlock();
}

// 槽函数：执行单条选择的指令
void HPDAQ_GUI::Slot_OnBtnCmdPedOnceRun()
{
	int i;
	i = ui.IDC_TABLE_VIEW_CMDPED->currentIndex().row();
	if ((i < 0)||(i>=CMDLIST_MAX_LEN))
		QMessageBox::warning(this, cs("Warning"), cs("Warinng: ") + cs("请选择正确的指令行!"));
	if(cmdlist.item[i].is_en==0)
		QMessageBox::warning(this, cs("Warning"), cs("Warinng: ") + cs("请选择正确的指令行!"));
	
	Send_Uart_Cmd(cmdlist.item[i].data, CMD_LEN);
	Put_Cmd_Log(cmdlist.item[i].cname, cmdlist.item[i].data,1,i);
}

// 在cmd log中显示发送的指令
// mode: 1时在cmdped中显示执行时间
void HPDAQ_GUI::Put_Cmd_Log(QString cname, u8 data[], u8 mode, u16 idx)
{
	QDateTime cur_qtime;
	QString s_cur_time;

	QString qs="";
	for (u8 i = 0; i < CMD_LEN; i++)
		qs = qs + mk_qstr(" %02X",data[i]);
	Show_Cmd_Log("Send cmd ["+qs+" ] ("+cname+")");

	if (mode)
	{
		cur_qtime = QDateTime::currentDateTime();
		s_cur_time = cur_qtime.toString("yyyyMMdd hh:mm:ss.zzz");
		p_view_cmdped->item(idx, 3)->setText(s_cur_time);
	}
}

// 槽函数：按钮执行list
bool is_pause_cont_valid=false;
void HPDAQ_GUI::Slot_OnBtnCmdPedListRun()
{
	cmdlist.cur_idx = 0;
	Show_Log("cmdlist Starting..");
	b_auto_cmdlist = true;
	cmdlist_state = S_RUN;
}

// 槽函数：按钮停止list
void HPDAQ_GUI::Slot_OnBtnCmdPedListStop()
{
	Show_Log("cmdlist Stopped.");
	b_auto_cmdlist = false;
	cmdlist_state = S_IDLE;
}

// 槽函数：按钮暂停/继续
void HPDAQ_GUI::Slot_OnBtnCmdPedListPauseCont()
{
	if (b_auto_cmdlist) // 如果正在进行，可以暂停
	{
		b_auto_cmdlist = false;
		is_pause_cont_valid = true;
		Show_Log("cmdlist Pause..");
		cmdlist_state = S_PAUSE;
	}
	else if ((!b_auto_cmdlist) && is_pause_cont_valid) // 如果被暂停过，可以开始
	{
		b_auto_cmdlist = true;
		Show_Log("cmdlist Continue..");
		cmdlist_state = S_RUN;
	}
}

// 复选框：单击消息
void HPDAQ_GUI::Slot_OnCkGetstatStateChanged(int state)
{
	QString data_log_filename = "";
	if (state == Qt::Checked) 
	{
		// 获取用户输入
		QString qstr;
		int data;
		Save_Usr_Conf();

		// 使能自动
		this_count = 0;
		b_auto_get = 1;
		Show_Log("设置为请求工参为:  自动");

		//创建data文件名
		QString s = Check_And_Create_Dir("_DATA");
		data_log_filename = s+"/data_log_"+(QDateTime::currentDateTime()).toString("DyyyyMMdd_Thhmmss")+".txt";
		Show_Log("Data File Path:");
		Show_Log("    " + data_log_filename);
	}
	else
	{
		b_auto_get = 0;
		Show_Log("设置为请求工参为:  手动");
		data_log_filename = "";
	}
}

void  HPDAQ_GUI::get_stat()
{
	u32 acc = 0;
	u8 i;
	u8 res;

	Fill_Cmd_Buf(tx_buf, 0xFF, 0x99, 0x11223344);

	res = Send_Uart_Cmd(tx_buf, CMD_LEN);

	if (!res)
		return;

	Get_Uart_Data_And_Show(rx_buf, RSP_LEN);
}

// 填充命令
void HPDAQ_GUI::Fill_Cmd_Buf(u8 tbuf[], u8 cmd_code, u8 cmd_type, u32 cmd_data)
{
	u32 acc = 0;
	u8 i;

	tbuf[0] = 0xEB;
	tbuf[1] = 0x90;
	tbuf[2] = cmd_code;
	tbuf[3] = cmd_type;
	tbuf[4] = (cmd_data >> 24) & 0xFF;;
	tbuf[5] = (cmd_data >> 16) & 0xFF;
	tbuf[6] = (cmd_data >> 8) & 0xFF;;
	tbuf[7] = (cmd_data >> 0) & 0xFF;;

	// 计算累加和
	for (i = 0; i < 8; i++)
		acc += tbuf[i];
	tbuf[8] = acc & 0xFF;
}

// 按钮：发送自定义指令
void HPDAQ_GUI::Slot_OnBtnCmdSend()
{
	// Prepare data from UI
	u32 d;
	u8 cmd_type;
	u32 cmd_data;

	QString qstr;
	uint32_t addr;
	uint32_t data = 0xDEADDEAD;

	// 获取用户输入
	qstr = ui.IDC_EDIT_CMD_TYPE->text();
	cmd_type = strtoul(qstr.toLatin1().data(), NULL, 16) & 0xFF;

	qstr = ui.IDC_EDIT_CMD_DATA->text();
	cmd_data = strtoul(qstr.toLatin1().data(), NULL, 16) & 0xFFFFFFFF;

	mutex.lock();
	Fill_Cmd_Buf(tx_buf,0x32, cmd_type, cmd_data);
	Send_Uart_Cmd(tx_buf, CMD_LEN);

	QString qs = "";
	for (u8 i = 0; i < CMD_LEN; i++)
		qs = qs + mk_qstr(" %02X", tx_buf[i]);
	Show_Cmd_Log("Send cmd [" + qs + cs(" ] (自定义)"));

	mutex.unlock();
}

// 发送指令（不应答）
// 返回值：1表示成功，0表示失败
u8 HPDAQ_GUI::Send_Uart_Cmd(u8 *buf, u8 len)
{
	DWORD ret;

	// 显示待发送命令
	SHOW_CMD_TO_LOG(buf);

	PurgeComm(h_com, PURGE_RXCLEAR); //清除接收缓存区
	WriteFile(h_com, buf, len, &ret, NULL);

	if (ret != len)
	{
		Show_Log("指令发送失败!");
		return 0;
	}
	else
		return 1;
}

// 接收数据
// 返回值：1表示成功，0表示失败
u8 HPDAQ_GUI::Get_Uart_Data_And_Show(u8 *buf, u16 len)
{
	DWORD ret;
	u8 retry_count = 0;

	memset(buf, 0, len);
	do {
		if (retry_count>100)
		{
			Show_Log("工参接收失败：超时? state.cbInQue=%d", state.cbInQue);
			return 0;
		}
		retry_count++;
		Sleep(1); //延迟
		ClearCommError(h_com, NULL, &state); //得到COM状态
	} while (state.cbInQue<len);

	if (len > RSP_LEN)
	{
		Show_Log("工参接收失败：长度不一致(len>RSP_LEN).");
		return 0;
	}
//	else
//		Show_Log("Got len=%d",len);

	//读取数据
	ReadFile(h_com, buf, len, &ret, NULL);

	//清空接受缓存区
	PurgeComm(h_com, PURGE_RXCLEAR); //清除接收缓存区

	if (ret != len)
	{
		Show_Log("工参接收失败：长度不一致.");
		return 0;
	}
	else
	{
		SHOW_REPLY_TO_LOG(buf);		// 显示数据到log
		Show_Stat_Data();			// 显示数据到界面
		return 1;
	}
}

// 显示工参数据到界面(更改为：累加和正确后，UDP组播出去)
int acc_err_cnt = 0;
void HPDAQ_GUI::Show_Stat_Data()
{
	static u32 daq_count = 0;
	
	u8 target_id;
	u32 acc;
	u16 i;

	// 先组个UDP包，等条件全部满足，最后再发送
	make_udp_buf(rx_buf, RSP_LEN, 4);

	if (fp_data_bin == NULL)
	{
		QString s = Check_And_Create_Dir("_DATA");
		QString data_file_name;
		data_file_name = "NUC_DATA_"+cur_qtime.toString("DyyyyMMdd_Thhmmss.bin");
		s = s + "/" + data_file_name;
		fp_data_bin = fopen((const char*)s.toLocal8Bit(), "wb");
		Show_Log("Data File Path:");
		Show_Log("    " + s);
	}
	fwrite(udp_packet.udp_buf,udp_packet.len, 1,fp_data_bin);

	// 工参包头检查
	if ((rx_buf[0] != 0xEB) || (rx_buf[1] != 0x90) ||
		(rx_buf[2] != 0xFF) || (rx_buf[3] != 0x99))
	{
		Show_Log("出错：工参包格式错误：包头不匹配");
		return;
	}

	// 工参累加和检查
	acc = 0;
	for (i = 0; i < RSP_LEN - 1; i++)
		acc += rx_buf[i];

	if (rx_buf[RSP_LEN - 1] != (acc & 0xFF))
	{
		acc_err_cnt++;
		Show_Log("出错：工参包格式错误：累加和不匹配，计算结果为:%02X---- %02X", acc & 0xFF, rx_buf[RSP_LEN - 1]);
		return;
	}
	Send_Mcast_UDP_Packet();
}

// 显示指令库
void HPDAQ_GUI::Show_Cmd_Lib(QString path)
{
	QModelIndex rootIndex;
	QStringList filter;

	if (fs_model)
		delete fs_model;

	Show_Log("Load cmdlib from: "+path);
	fs_model = new QFileSystemModel();
	fs_model->setRootPath("/");
	//rootIndex = fs_model->index(R"(E:/TestDir)"); // 设置View显示的root
	rootIndex = fs_model->index(path); // 设置View显示的root
    // 设置过滤器
	filter <<"*.cmdlst" << "*.bin" ;
	fs_model->setNameFilters(filter);
	fs_model->setNameFilterDisables(false); 

   // 将TreeView与FileSysyemModel绑定
	ui.IDC_TREE_VIEW->setAnimated(false);
	ui.IDC_TREE_VIEW->setSortingEnabled(true);
	ui.IDC_TREE_VIEW->setModel(fs_model);
	ui.IDC_TREE_VIEW->setRootIndex(rootIndex);
	ui.IDC_TREE_VIEW->header()->resizeSection(0, 400);
	//ui.IDC_TREE_VIEW->setUniformRowHeights(1);

	// 设置QFileSystemModel遍历完毕后，发起expandAll() -- 是否需要
	//connect(fs_model, SIGNAL(directoryLoaded(const QString &)), ui.IDC_TREE_VIEW, SLOT(expandAll()));

	// 相应鼠标slot函数
	connect(ui.IDC_TREE_VIEW, SIGNAL(clicked(QModelIndex)), this, SLOT(Slot_onMyClicked(QModelIndex)));
	connect(ui.IDC_TREE_VIEW, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(Slot_onMyDoubleClicked(QModelIndex)));

	//可弹出右键菜单
	ui.IDC_TREE_VIEW->setContextMenuPolicy(Qt::CustomContextMenu);
	connect(ui.IDC_TREE_VIEW, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(Slot_ContextMenu(QPoint)));
}

// 槽函数：右键弹出菜单
void HPDAQ_GUI::Slot_ContextMenu(const QPoint &pos)
{
//	Show_Log("Slot_ContextMenu");

	QMenu menu;
	QModelIndex curIndex = ui.IDC_TREE_VIEW->indexAt(pos);   //当前点击的元素的index
	QModelIndex index = curIndex.sibling(curIndex.row(), 0); //该行的第1列元素的index
	if (index.isValid())
	{
		menu.addAction(QStringLiteral("指令查看"), this, SLOT(Slot_TreeMenu_CmdView()));
		menu.addSeparator();    //添加一个分隔线
		menu.addAction(QStringLiteral("指令添加"), this, SLOT(Slot_TreeMenu_CmdAdd()));
	}
	menu.exec(QCursor::pos());  //显示菜单
}

// 槽函数：右键选择/查看指令
void HPDAQ_GUI::Slot_TreeMenu_CmdView()
{
	QString qs;
	FILE *fp_view;

	QModelIndex curIndex = ui.IDC_TREE_VIEW->currentIndex();
	QModelIndex index = curIndex.sibling(curIndex.row(), 0); //同一行第一列元素的index
	if (index.isValid())
	{
		QString filepath = fs_model->filePath(curIndex);  // 获取全路径
		QFileInfo fileinfo(filepath);
		if (fileinfo.isFile()) // 如果选择的是文件, 则显示
		{
			if (filepath.endsWith(".bin")) // 这是一个二进制文件,按照二进制方式显示
			{
				QByteArray bdata;
				QFile file(filepath);
				if (!file.open(QIODevice::ReadOnly))
					return;
				bdata = file.readAll();
				file.close();
				u16 n=bdata.size();
				qs = "";
				for (u16 i = 0; i < n; i++)
				{
					if (i % 8 == 0)
						qs = qs + mk_qstr("\n%02X: ",i);
					qs = qs + mk_qstr("%02X ", bdata[i] & 0xFF);
				}

				QMessageBox messageBox(
					QMessageBox::Question, 
					cs("指令文件信息"), 
					filepath + cs(":\n") + qs,
					QMessageBox::Ok);
				messageBox.setStyleSheet("QLabel{"
					"font-size:28px;"
					"font-family:FangSong;"
					"qproperty - alignment: AlignLeft"
					"}");
				messageBox.exec();
			}

			if (filepath.endsWith(".cmdlst")) // 这是一个cmdlst, 打开文本显示
				QProcess::startDetached("C:\\Windows\\system32\\notepad.exe", { filepath });
		}
		else  // 如果选择的是文件夹,则提示选择出错
		{
			QMessageBox::information(this,
				cs("信息"),
				cs("这是一个文件夹, 不是一个文件!\n\n") + filepath + cs("\n\n请重新选择指令文件!"));
		}
	}
}

// 判断该行是否为注释行(即空白或制表符之后的首字符为#)
bool is_comment_or_empty_line(const char *s)
{
	int n;
	int i = 0;

	n = strlen(s);

	while ((s[i] == ' ') || (s[i] == '	')) // 忽略开始的空格或者Tab
		i++;

	if (s[i] == '#') // 注释行
		return 1;
	
	if (s[i] == '\n')  // 空行
		return 1;

		return 0;
}

// 从类似如下字符串中截取 "指令名称1" (即#后面的[]内部的字符串)
//QString qs = cs("<1> EB90 33 06 11111111 00        # [指令名称1]和其他注释");
QString get_mystr(QString qs)
{
	QString res = "";
	QString qt;
	int len;
	int idx;
	int idx1, idx2;

	len = qs.length();
	idx = qs.indexOf('#');
	if (idx < 0)
		goto __end;
	qt = qs.right(len - idx);
	idx1 = qs.indexOf('[');
	idx2 = qs.indexOf(']');
	if ((idx1 < 0) || (idx2<0) || (idx2 <= idx1))
		goto __end;
	res = qs.mid(idx1 + 1, idx2 - idx1 - 1);
__end:
	return res;
}


// 从cmdlist文件filepath加载指令序列(含1条或多条)
// 成功：返回true，否则返回false
bool HPDAQ_GUI::Load_CmdLst_From_File(QString filepath)
{
	QByteArray ba;
	char *sfilename;
	FILE *fp;
	char line_buf[1024];
	int n;
	QString qs;
	u16 delay;
	u32 d[5];
	u16 head;
	u8 id;
	u8 adr;
	u32 word;
	bool res=true;

	// QString转换成char*
	ba = (filepath.toLocal8Bit()).data();
	sfilename = ba.data();

	QFileInfo fileInfo(filepath);
	QString baseName = fileInfo.baseName();
	QString cmdname;

	fp = fopen(sfilename, "r");
	while (fgets(line_buf, 1024, fp) != NULL)
	{
		cmdlist.item[cmdlist.tot_cnt].is_en = 0;

		//	Show_Log(line_buf);
		if (is_comment_or_empty_line(line_buf))  //如果是注释行，跳转到下一行
			continue;

		// 解析这一行的数据
		n = sscanf(line_buf, "<%d> %X %X %X %X", &d[0], &d[1], &d[2], &d[3], &d[4]);
		delay = d[0];
		head = d[1];
		id = d[2];
		adr = d[3];
		word = d[4];

		if ((n != 5)||(head!=0xEB90))  //出错了,强制结束
		{
			//Show_Log("加载[出错], 第%d行内容为: %s", cmdlist.tot_cnt, line_buf);
			qs = QString::asprintf("line %d:\n %s                     .", cmdlist.tot_cnt, line_buf);
			QMessageBox::warning(this, cs("Load Error"), cs("出错 ") + qs + "\n 加载指令不完整，请确认是否继续执行序列.");			
			res = false;
			goto __end;
		}
		else
		{
			Show_Log("加载[成功], 第%d行内容为: <%d> %04X %02X %02X %08X", cmdlist.tot_cnt, delay, head, id, adr, word );
			Fill_Cmd_Buf(cmdlist.item[cmdlist.tot_cnt].data, id, adr, word);
			cmdlist.item[cmdlist.tot_cnt].gap = delay;
			cmdlist.item[cmdlist.tot_cnt].is_en = 1;
			cmdname = get_mystr(line_buf);
			if(cmdname=="")
				cmdlist.item[cmdlist.tot_cnt].cname = baseName+"/"+mk_qstr("无名");
			else
				cmdlist.item[cmdlist.tot_cnt].cname = baseName + "/" + cmdname;
			cmdlist.tot_cnt++;
		}
	}
	
__end:
	Show_CmdPed();
	fclose(fp);
	return res;
}

// 槽函数：右键选择/发送指令
void HPDAQ_GUI::Slot_TreeMenu_CmdAdd()
{
	QModelIndex curIndex = ui.IDC_TREE_VIEW->currentIndex();
	QModelIndex index = curIndex.sibling(curIndex.row(), 0); //同一行第一列元素的index
	if (index.isValid())
	{
		QString filepath = fs_model->filePath(curIndex);  // 获取全路径
		QFileInfo fileinfo(filepath);  
		if (fileinfo.isFile()) // 如果选择的是文件, 则确认和发送
		{
			int res = QMessageBox::question(this,
				cs("询问"),
				cs("你确定要添加如下指令吗?\n\n") + filepath,
				QMessageBox::Yes | QMessageBox::No,
				QMessageBox::No);

			if (res != QMessageBox::Yes)
				return;

			///////////////////////////////// 
			       // 指令发送流程 //
			///////////////////////////////// 
			if (filepath.endsWith(".bin")) // 这是一个bin, 直接发送
			{
				// TO BE ADDED
				Show_Log("暂时不支持bin文件");
			}
			if (filepath.endsWith(".cmdlst")) // 这是一个cmdlst, 加载并启动执行
			{
				bool res=Load_CmdLst_From_File(filepath);
			}
		}
		else  // 如果选择的是文件夹,则提示选择出错
		{
			QMessageBox::information(this, 
				cs("信息"), 
				cs("这是一个文件夹, 不是一个文件!\n\n") + filepath+cs("\n\n请重新选择指令文件!"));
		}
	}
}

//Btn: Debug
void HPDAQ_GUI::Slot_OnBtnDebug()
{	
#if 0
	u8 d;
	bool is_ok;

	QString qs="11 2 33 4		55 6 77 8 99 A       BB C DD E    ff";
	QStringList str = qs.split(QRegExp("\\s+")); // \s+表示至少一个空格
	foreach(QString s, str)
	{
		d = s.toUInt(&is_ok, 16);
		Show_Log("%02X",d);
	}
#endif
}



// 将ASIC格式文本,转换为bin
// 输入txt格式如下: (可空格\Tab\回车隔开,可单个字节用一个字符)
//     11 2 33 4		55 6 77 8 99 A       BB C DD E    ff
u32 HPDAQ_GUI::get_data_from_qs_to_buf(u8 *pbuf, QString line, u8 is_new)
{
	static u32 idx = 0;
	u8 d;
	bool is_ok;

	if (is_new)
		idx = 0;
	
	QStringList str = line.split(QRegExp("\\s+")); // \s+表示至少一个空格
	qDebug()<<str.length();

	foreach(QString s, str)
	{
		d = s.toUInt(&is_ok, 16);
		if(is_ok)
			pbuf[idx++] = d;
	}
	return idx;
}

void HPDAQ_GUI::Slot_OnZKBtnCfgTxt2Bin()
{
	QString cfg_filename;
	u32 bin_len=0;
	u8 is_new=1;

	
	memset(zk_cfg_buf,0, ZK_CFG_LEN);

	cfg_filename = QFileDialog::getOpenFileName(
		this,
		"Open File",
		".",
		"Dat Files (*.txt);;All(*.*)"
	);
	if (cfg_filename.isEmpty()) {
		Show_Log("Warning: 未选择cfg文件.");
		return;
	}
	else
		Show_Log(cs("选择了cfg文件: ") + cfg_filename);

	QFile file(cfg_filename);
	bool isOk = file.open(QIODevice::ReadOnly | QIODevice::Text);
	if (!isOk)
	{
		Show_Log("转换出错,错误码=-1");
		return;
	}

	Show_Log("开始转换...");
	QString line_buf;
	QTextStream in(&file);
	int line_cnt = 0;
	while (!in.atEnd())
	{
		line_buf = in.readLine();
		line_cnt++;
		bin_len=get_data_from_qs_to_buf(zk_cfg_buf,line_buf,is_new); // 执行转换
		is_new = 0;
	}
	//关闭文件
	file.close();

	//输出文件名
	FILE *fp_bin; // 将bin文件输出
	QString cfg_filename_bin;
	cfg_filename_bin = cfg_filename + ".bin";
	
	QByteArray ba = cfg_filename_bin.toLocal8Bit();
	char *s_bin_file_name = ba.data();

	//fp_bin = fopen("111.bin", "wb");
	fp_bin = fopen(s_bin_file_name, "wb");
	if (fp_bin == NULL)
		Show_Log("文件打开失败");

	if (bin_len > ZK_CFG_LEN)
		Show_Log("出错：bin_len=%d",bin_len);
	fwrite(zk_cfg_buf, bin_len, 1, fp_bin);
	fclose(fp_bin);
	Show_Hex_Info(mk_qstr("数据(line=%d，size=%d)为:",line_cnt, bin_len), zk_cfg_buf, bin_len);
	Show_Log(cs("转换存储为: ") + mk_qstr("%s",s_bin_file_name));	
}

// 槽函数：鼠标单击
void HPDAQ_GUI::Slot_onMyClicked(const QModelIndex &qm)
{
#if 0
	Show_Log("单击:");
	Show_Log(cs("文件名  : ")+ fs_model->fileName(qm));
	Show_Log(cs("文件路径: ") + fs_model->filePath(qm));
	Show_Log(cs("文件大小: ") + qm.data().toString());
#endif
}

// 槽函数：鼠标双击
void HPDAQ_GUI::Slot_onMyDoubleClicked(const QModelIndex &qm)
{
#if 0
	Show_Log("双击:");
	Show_Log(cs("文件名  : ") + fs_model->fileName(qm));
	Show_Log(cs("文件路径: ") + fs_model->filePath(qm));
	Show_Log(cs("文件大小: ") + qm.data().toString());
#endif
}

/////////////////////////////////////////////////
// 定时器函数timerEvent(QTimerEvent *t)自动调用
// 在InitWindow()里面调用startTimer来开启本定时器.
/////////////////////////////////////////////////
void HPDAQ_GUI::timerEvent(QTimerEvent *t)
{
	if (t->timerId() == timer_id)
	{
		QDateTime time;
		QString str;
		time = QDateTime::currentDateTime();//获取系统现在的时间
		str = time.toString("yyyyMMdd hh:mm:ss"); //设置显示格式
		ui.IDC_LABEL_TIME->setText(str);//在标签显示
	
		//////////////////////////////////
		//Slot_OnBtnDebug(); // Test ONly
		//////////////////////////////////
		if (h_com == INVALID_HANDLE_VALUE)// 判断串口打开
			return;

		// 轮询工参
		if (b_auto_get)
		{
			mutex.lock();
			get_stat();
			mutex.unlock();
		}
		
		switch (cmdlist_state)
		{
		case S_IDLE: ui.IDC_LABEL_CMDLIST_RUNSTATE->setText(cs("空闲")); break;
		case S_RUN: ui.IDC_LABEL_CMDLIST_RUNSTATE->setText(cs("进行中")); break;
		case S_PAUSE: ui.IDC_LABEL_CMDLIST_RUNSTATE->setText(cs("暂停")); break;
		default:
			break;
		}
	}
}

// 检查是否加入组播，若加入则退出
void HPDAQ_GUI::Check_And_Leave_Mcast()
{
	if (udpSocket->state() == QAbstractSocket::BoundState)
	{
		udpSocket->leaveMulticastGroup(g_mcast_addr);//退出组播组ip
		udpSocket->abort();
		Show_Log(cs("退出组播:") + g_mcast_addr.toString());
	}
}

// 组播发送一个UDP包
// 包格式: BBBBBBBB TTTTTTTT LLLL [DATA MAX=512] EEEEEEEE
void HPDAQ_GUI::Send_Mcast_UDP_Packet()
{
	if (!is_udp_init_done)
	{
		is_udp_init_done = true;

		// 如果已经有套接字,先退出组播并销毁
		if (udpSocket)
		{
			Check_And_Leave_Mcast();
			delete(udpSocket);
		}

		g_mcast_addr = QHostAddress(s_mcast_ip);

		// 创建套接字并加入组播
		udpSocket = new QUdpSocket(this);
		udpSocket->setSocketOption(QAbstractSocket::MulticastTtlOption, 1); // TTL设置为1
		udpSocket->joinMulticastGroup(g_mcast_addr);
		Show_Log(cs("加入组播:") + g_mcast_addr.toString());
	}

	// 发送
	QDateTime qdt1(QDate(2025, 12, 30), QTime(0, 0, 0)); // timeout
	if (cur_qtime.secsTo(qdt1) > 0)
	{
		udpSocket->writeDatagram((char*)(udp_packet.udp_buf), udp_packet.len, g_mcast_addr, MULT_PORT);
		Show_Hex_Info(mk_qstr("mbCast %d-byte UDP packet:", udp_packet.len), udp_packet.udp_buf, udp_packet.len);
	}
}

// 创建一个UDP包
void HPDAQ_GUI::make_udp_buf(u8 *packet_buf, u16 pakcet_len, u16 data_offset)
{
	u32 bdt_u32 = BDT_U32_FROM_BJT_QDT(QDateTime::currentDateTime());
	memset(udp_packet.udp_buf, 0, UDP_LEN);

	if (pakcet_len > 1000)
	{
		qDebug()<<"Error in make_udp_buf(): pakcet_len="<< pakcet_len <<">"<< 1000;
		return;
	}

	if (data_offset > 32)
	{
		qDebug() << "Error in make_udp_buf(): data_offset=" << data_offset << ">" << 32;
		return;
	}
	
	udp_packet.len = pakcet_len + 16;

	//HEAD
	udp_packet.udp_buf[0] = 0xBB;
	udp_packet.udp_buf[1] = 0xBB;
	udp_packet.udp_buf[2] = 0xBB;
	udp_packet.udp_buf[3] = 0xBB;
	
	//DATA TIME (BDT format)
	udp_packet.udp_buf[4] = (bdt_u32 >> 24) & 0xFF;
	udp_packet.udp_buf[5] = (bdt_u32 >> 16) & 0xFF;
	udp_packet.udp_buf[6] = (bdt_u32 >> 8) & 0xFF;
	udp_packet.udp_buf[7] = (bdt_u32 >> 0) & 0xFF;
	
	// LEN and offset
	udp_packet.udp_buf[8] = (pakcet_len >> 8) & 0xFF;
	udp_packet.udp_buf[9] = pakcet_len & 0xFF;
	udp_packet.udp_buf[10] = ((data_offset+12) >> 8) & 0xFF;
	udp_packet.udp_buf[11] = (data_offset+12) & 0xFF;

	// 拷贝packet到UDP数据域
	memcpy(udp_packet.udp_buf + 12, packet_buf, pakcet_len);

	//END
	udp_packet.udp_buf[pakcet_len +16 - 4] = 0xEE;
	udp_packet.udp_buf[pakcet_len +16 - 3] = 0xEE;
	udp_packet.udp_buf[pakcet_len +16 - 2] = 0xEE;
	udp_packet.udp_buf[pakcet_len +16 - 1] = 0xEE;
}

/////////////////////////////////////////////////
//窗口关闭时自动调用
/////////////////////////////////////////////////
void HPDAQ_GUI::closeEvent(QCloseEvent * event)
{
	b_cmdlist_run_flag = false;
	qm.lock();
	if (udpSocket)
	{
	//	Check_And_Leave_Mcast();
	//	delete(udpSocket);
	}
}